`include "common.svh"
import "DPI-C" function void difftest_skip();
import "DPI-C" function void difftest_step(
  input longint pc,
  input int inst,
  input longint registers[`ARF_SIZE]
);
import "DPI-C" function void difftest_reset();

module difftest #(
    parameter COMMIT_WIDTH = 2
) (
    input clk,
    input rst

);

  `define REGS inst_backend.inst_regfile.inst_regfile.read_regs
  `define COMMIT inst_backend.inst_commit
  /* verilator lint_off UNOPTFLAT */
  longint registers[COMMIT_WIDTH][`ARF_SIZE]  /*verilator public_flat*/;
  PRF_IDX arch_rat[`ARF_SIZE];
  PRF_IDX arch_rat_next[COMMIT_WIDTH][`ARF_SIZE];
  word_t phyreg[(2**`PRF_AW)-1:0]  /*verilator public_flat*/;
  assign phyreg = `REGS;
  genvar i;
  generate
    for (i = 0; i < COMMIT_WIDTH; i = i + 1) begin : Commit
      PRF_IDX arch_rat_now[`ARF_SIZE];
      assign arch_rat_now = (i == 0) ? arch_rat : arch_rat_next[i-1];
      always_comb begin
        integer idx;
        arch_rat_next[i] = arch_rat_now;
        if (`COMMIT.i_valid && `COMMIT.i_op_valid[i]) begin
          if (`COMMIT.i_op[i].rop.fop.dop.has_rd) begin
            arch_rat_next[i][`COMMIT.i_op[i].rop.fop.dop.rd] = `COMMIT.i_op[i].rop.pdst;
          end
        end
        for (idx = 0; idx < `ARF_SIZE; idx = idx + 1) begin
          registers[i][idx] = `REGS[arch_rat_next[i][idx]];
        end
      end
    end

    always_ff @(posedge clk) begin
      integer idx;
      if (rst) begin
        for (idx = 0; idx < `ARF_SIZE; idx = idx + 1) begin
          arch_rat[idx] <= idx[`PRF_AW-1:0];
        end
      end else begin
        arch_rat <= arch_rat_next[COMMIT_WIDTH-1];
      end
    end
  endgenerate

  always_ff @(posedge clk) begin
    integer idx;
    if (rst) begin
      difftest_reset();
    end else begin
      if (`COMMIT.i_valid) begin
        for (idx = 0; idx < COMMIT_WIDTH; idx = idx + 1) begin
          if (`COMMIT.i_op_valid[idx] & (!`COMMIT.i_op_state[idx].replay)) begin
            if(`COMMIT.i_op_state[idx].mmio | `COMMIT.i_op[idx].rop.fop.dop.fu_type == FU_MISC) difftest_skip();
            difftest_step(`COMMIT.i_op[idx].rop.fop.pc, `COMMIT.i_op[idx].rop.fop.inst,
                          registers[idx]);
          end
        end
        // for (idx = 0; idx < `ARF_SIZE; idx = idx + 1) begin
        //   $display("R[%d] 0x%016x", idx, `REGS[arch_rat_read[COMMIT_WIDTH-1][idx]]);
        // end
      end
    end
  end
endmodule
